﻿// ===============================================================================
// Form.cs
// EOS Web Framework - Demo
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Web.Mvc.Controls
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Reflection;
    using System.Web;
using EOS.Framework.Tools.Aspects;


     
    public abstract class Form<TContainer> : MarkupControl, IForm where TContainer : Container
    {
        const string MvcComponent = "Mvc.Component";

        private string _uri;
        private TContainer _container;
        private bool _enable = true;
        private List<IInputControl> _controls = new List<IInputControl>();

        public event EventHandler<InputCastExceptionEventArgs> InputCastExceptionOccured;

        public bool Submitted
        {
            get
            {
                return HttpContext.Current.Request.Form[MvcComponent] != null &&
                       HttpContext.Current.Request.Form[MvcComponent].Equals(_container.Name);
            }
        }

        public bool Enable
        {
            get { return _enable; }
            set { _enable = value; }
        }

        public TContainer Container
        {
            get { return _container; }
        }

        public bool IsValid
        {
            get
            {
                bool isValid = true;
                foreach (IInputControl control in _controls)
                {
                    IValidatedInputControl validatedControl = control as IValidatedInputControl;
                    if (validatedControl != null && !validatedControl.IsValid)
                    {
                        isValid = false;
                        break;
                    }
                }

                return isValid;
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1056")]
        public string Uri
        {
            get { return _uri; }
            set { _uri = value; }
        }

        void Markup_ControlAttached(object sender, ControlAttachedEventArgs e)
        {
            IInputControl ic = e.Control as IInputControl;
            if (ic != null)
            {
                _controls.Add(ic); 
            }
        }

        public override void SetMarkup(IMarkup markup)
        {
            base.SetMarkup(markup);
            Markup.ControlAttached += new EventHandler<ControlAttachedEventArgs>(Markup_ControlAttached);
        }

        protected override void OnAttached()
        {
            _container = Parent.Container as TContainer;
            if (_container == null)
            {
                throw new InvalidOperationException();
            }
            else
            {
                Uri = Container.Context.Url;
            }

            base.OnAttached();
        }

        public string GetValue(string name)
        {
            return HttpContext.Current.Request.Form[name];
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public TValue GetValue<TValue>(string name)
        {
            TValue result = default(TValue);
            try
            {
                result = (TValue)Injection.ChangeType(typeof(TValue), HttpContext.Current.Request.Form[name]);
            }
            catch (InvalidCastException)
            {
            }

            return result;
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public FormValueDictionary GetValueCollection(string name)
        {
            FormValueDictionary values = new FormValueDictionary();
            foreach (string key in HttpContext.Current.Request.Form)
            {
                try
                {
                    if (key.StartsWith(name + ".", StringComparison.CurrentCulture))
                    {
                        values.Add(key.Substring(key.IndexOf(".", StringComparison.CurrentCulture) + 1), HttpContext.Current.Request.Form[key]);
                    }
                }
                catch
                {
                }
            }

            return values;
        }

        protected abstract void BeginRender();

        [SuppressMessage("Microsoft.Design", "CA1031")]
        protected override void OnRender(IXhtmlWriter writer, PageContext context)
        {
            if (Markup == null)
            {
                throw new InvalidOperationException();
            }

            if (Submitted)
            {
                Type type = GetType();
                foreach (MethodInfo method in type.GetMethods())
                {
                    if (method.Name == "OnSubmit")
                    {
                        ParameterInfo[] parameters = method.GetParameters();

                        FormValueDictionary formValues = new FormValueDictionary(context.HttpContext.Request.Form);
                        if (formValues.ContainsKey(MvcComponent))
                        {
                            formValues.Remove(MvcComponent);
                        }
                        int formValuesIndex = -1;

                        object[] parameterValues = new object[parameters.Length];
                        for (int i = 0; i < parameters.Length; i++)
                        {
                            ParameterInfo p = parameters[i];
                            if (p.ParameterType.Equals(typeof(GroupedFormValueDictionary)))
                            {
                                GroupedFormValueDictionary formValueArray = new GroupedFormValueDictionary();
                                foreach (string key in context.HttpContext.Request.Form)
                                {
                                    if (key.StartsWith(p.Name + ".", StringComparison.CurrentCulture))
                                    {
                                        formValueArray.Add(key.Substring(key.IndexOf(".", StringComparison.CurrentCulture) + 1), formValues[key]);
                                        formValues.Remove(key);
                                    }
                                }
                                parameterValues[i] = formValueArray;
                            }
                            else if (context.HttpContext.Request.Form[p.Name] != null)
                            {
                                try
                                {
                                    parameterValues[i] = Injection.ChangeType(p.ParameterType,
                                        context.HttpContext.Request.Form[p.Name]);
                                }
                                catch (Exception)
                                {
                                    if (InputCastExceptionOccured != null)
                                    {
                                        InputCastExceptionOccured(this, new InputCastExceptionEventArgs(p.Name));
                                    }

                                    parameterValues[i] = null;
                                }

                                formValues.Remove(p.Name);
                            }
                            else if (p.ParameterType.Equals(typeof(FormValueDictionary)))
                            {
                                formValuesIndex = i;
                            }
                            else
                            {
                                if (p.ParameterType.IsValueType)
                                {
                                    parameterValues[i] = Activator.CreateInstance(p.ParameterType);
                                }
                                else
                                {
                                    parameterValues[i] = null;
                                }
                            }
                        }

                        if (formValuesIndex >= 0)
                        {
                            parameterValues[formValuesIndex] = formValues;
                        }

                        method.Invoke(this, parameterValues);
                    }
                }
            }

            BeginRender();

            if (_enable)
            {
                writer.WriteStartElement("form");
                if (!string.IsNullOrEmpty(_uri))
                {
                    writer.WriteAttribute("enctype", "multipart/form-data");
                    writer.WriteAttribute("method", "post");
                    writer.WriteAttribute("action", ToAbsolutePath(_uri));
                }
                Markup.RenderMarkup(writer, context);

                writer.WriteStartElement("input");
                writer.WriteAttribute("type", "hidden");
                writer.WriteAttribute("name", MvcComponent);
                writer.WriteAttribute("value", _container.Name);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }
        }

        public void Reset()
        {
            foreach (IInputControl control in _controls)
            {
                control.Reset();
            }
        }
    }
}
